/*******************************************************************************
 * This file is part of blueprint4j www.blueprint4j.org.
 * 
 *     blueprint4j is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     blueprint4j is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with blueprint4j. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
/**
* This is an abstract message class no fields have been defined in this
* class
* $Log: MessageISOField.java,v $
* Revision 1.1.1.1  2004/04/22 12:56:14  travisb
* Fir
*
* Revision 1.1.1.1  2003/12/21 09:04:08  jaspervdb
* no message
*
* Revision 1.1.1.1  2003/10/20 15:30:48  travisb
* First import into CVS at Cellstop
*
* Revision 1.1.1.1  2003/03/30 15:25:28  tbulford
* no message
*
* Revision 1.1  2003/01/12 08:30:34  travis
* Import x MANY
*
* Revision 1.1.1.1  2002/12/16 22:14:01  jasper
* no message
*
* Revision 1.2  2002/09/29 17:17:15  travisb
* Template Processor Changes
*
* Revision 1.1  2002/02/20 13:30:43  JasperV
* First time add to new Applications layer
*
* Revision 1.1  2002/01/24 14:18:14  travisb
* First Version of the Application Layer
*
*/

package blueprint4j.messages;

import java.io.*;
import java.util.*;
import java.text.*;

abstract public class MessageISOField extends Message {
	protected int save_fields = 129;
	protected Field fields[] = new Field[ 130 ];
	protected Field message_type;
	protected Date date_last_modified = new Date();
	protected String name = "Recieved";
	protected static SimpleDateFormat date_format = new SimpleDateFormat("yyyy.MM.dd hh:mm:ss");

	public MessageISOField()
	throws ExceptionMessage {
		// Clear all fields
		for (int t = 0;t < fields.length;t++) {
			fields[t] = null;
		}

		// Create needed fields message type allowed to be anything we like
		fields[ 129 ] = message_type = new FieldANS( Field.LENGTH_FIXED, 4 , -1 , "Type");
		fields[ 0 ] = new FieldB( Field.LENGTH_FIXED, 8 , 0 , "BitField1");
		fields[ 1 ] = new FieldB( Field.LENGTH_FIXED, 8 , 1 , "BitField2");
	}

	/**
	* Sets all the message details from a properties file
	* @param props
	* the properties are formatted as follows <br>
	* <code><br>
	* Type=0100 <br>
	* Field_3=220000 <br>
	* Field_4=2900 <br>
	* Field_7=0921182451 <br>
	* </code>
	*/
	public void setMessage(Properties props)
	throws ExceptionMessage {
		date_last_modified = new Date();
		// Set type
		String type = "" + props.getProperty("Type");
		if (type == null) {
			throw new ExceptionMessage("Field 'Type' not found in properties.");
		}
		try {
			setType(type);
		} catch (NumberFormatException nfe) {
			throw new ExceptionMessage("Field 'Type' found but value [" + type + "] is not valid.");
		}
		// Set this message name
		setName(props.getProperty("Name"));
		// Set fields from properties
		for (int t = 2;t < save_fields;t++) {
			// Set field to null
			if (fields[t] != null) {
				set(t, null);
			}
			// Get new value
			String v = props.getProperty("Field_" + t);
			if (v != null) {
				if (v.equalsIgnoreCase("{auto}")) {
					set(t, fields[t].getFieldValueDefault());
				} else {
					set(t, v);
				}
			}
		}
	}


	/**
	* Returns all the message details in properties file
	* the properties are formatted as follows <br>
	* <code><br>
	* Type=0100 <br>
	* Field_3=220000 <br>
	* Field_4=2900 <br>
	* Field_7=0921182451 <br>
	* </code>
	*/
	public Properties getMessage()
	throws ExceptionMessage {
		Properties props = new Properties();
		// Set type
		props.setProperty("Name", getName());
		if (message_type != null) {
			props.setProperty("Type", new String(message_type.getPadded(4)));
		}
		// Set fields from properties
		for (int t = 2;t < save_fields ;t++) {
			if (!fields[t].isNull()) {
				String f = get(t);
				if (f != null) {
					props.setProperty("Field_" + t, f);
				}
			}
		}
		// Return the properties
		return props;
	}


	/**
	* Returns all the message details in properties string
	* <code><br>
	* Type=0100 <br>
	* Field_3=220000 <br>
	* Field_4=2900 <br>
	* Field_7=0921182451 <br>
	* </code>
	*/
	public String getMessageAsString()
	throws ExceptionMessage {
		String value = "";
		// Set type
		value += "Name=" + getName() + "\n";
		if (message_type != null) {
			value += "Type=" + new String(message_type.getPadded(4)) + "\n";
		}
		// Set fields from properties
		for (int t = 2;t < save_fields;t++) {
			if (fields[t] != null) {
				String f = get(t);
				if (f != null) {
					value += "Field_" + t + "=" + f + "\n";
				}
			}
		}
		// Return the properties
		return value;
	}


	/**
	* Overloaded to return a more suitable name
	*/
	public String toString() {
		try {
			if (date_last_modified != null) {
				if (message_type != null) {
					if (message_type.getPadded(4) != null) {
						return "Message[" + new String(message_type.getPadded(4)) + "] " + name + " @ " + date_format.format(date_last_modified);
					}
				} else {
					return "Message[" + name + " @ " + date_format.format(date_last_modified);
				}

			}
		} catch (ExceptionMessage em) {}
		return "Message badly formated";
	}


	/**
	* Set this message name
	* @param
	*/
	public void setName(String p_name) {
		if (p_name == null) {
			name = "Recieved";
		} else {
			name = p_name;
		}
	}


	/**
	* Set this message name
	*/
	public String getName() {
		return name;
	}


	/**
	* Work out field number from name
	*/
	private int getFieldByName(String name)
	throws ExceptionMessage {
		for (int t = 0;t < 130;t++) {
			if (fields[t] != null && name.equalsIgnoreCase(fields[t].getName())) {
				return t;
			}
		}
		return -1;
	}

	/**
	* Sets a field definition
	* @param
	*/
	protected void setField( Field as_field )
	throws ExceptionMessage {
		int field_number = as_field.getFieldNumber();
		if ( field_number < 2 || field_number > 128 ) {
			throw new ExceptionMessage( "Attempt to set a field definition out of range (2-128)" );
		}
		fields[ field_number ] = as_field;
	}

	/**
	* Check is a field is defined
	* @param
	*/
	public boolean hasField( int field )
	throws ExceptionMessage {
		if ( field < 2 || field > 128 ) {
			throw new ExceptionMessage( "Attempt to check hasField out of range (2-128)" );
		}
		return fields[ field ] != null;
	}


	/**
	* Check is a field is defined
	* @param
	*/
	public boolean hasField( String field )
	throws ExceptionMessage {
		return (getFieldByName(field) != -1);
	}


	/**
	* Get the max width for the field
	* @param
	*/
	public int getMaxFieldWidth( int field )
	throws ExceptionMessage {
		if ( field < 2 || field > 128 ) {
			throw new ExceptionMessage( "Attempt to get field max width out of range (2-128)" );
		}
		if (fields[ field ] == null) {
			throw new ExceptionMessage( "Attempt to get field max width for uindefined field" );
		}
		return fields[ field ].getFieldSize();
	}


	/**
	* Get the max width for the field
	* @param
	*/
	public int getMaxFieldWidth( String field )
	throws ExceptionMessage {
		return getMaxFieldWidth(getFieldByName(field));
	}

	/**
	* Sets the iso message type
	* @param message_type takes a hex value for the type of message
	* therfore 0800 - 0x0800
	*/
	public void setType(String p_message_type )
	throws ExceptionMessage {
		if (message_type == null) {
			throw new ExceptionMessage("Attempt to set message type, but no field for message type was defined");
		}
		message_type.set( p_message_type );
		date_last_modified = new Date();
	}

	/**
	* Returns the iso message type
	* @return message_type takes a hex value for the tywe of message
	* therfore 0800 - 0x0800
	*/
	public String getType()
	throws ExceptionMessage {
		if (message_type == null) {
			throw new ExceptionMessage("Attempt to get message type, but no field for message type was defined");
		}
		String type = message_type.get();
		if (type == null) return "";
		return type;
	}

	public String get( int field_no )
	throws ExceptionMessage {
		if ( fields[ field_no ] == null ) {
			throw new ExceptionMessage( "Field " + field_no + " was asked for, but not defined" );
		}
		return fields[ field_no ].get();
	}

	public String get( String field )
	throws ExceptionMessage {
		return get(getFieldByName(field));
	}

	public void set( int field_no, String p_value )
	throws ExceptionMessage {
		if ( fields[ field_no ] == null ) {
			throw new ExceptionMessage( "Field " + field_no + " was asked for, but not defined" );
		}
		date_last_modified = new Date();
		fields[ field_no ].set( p_value );
	}

	public void set( String field, String p_value )
	throws ExceptionMessage {
		set(getFieldByName(field), p_value);
	}

	public void setBinary( int field_no, byte[] p_value )
	throws ExceptionMessage {
		if ( fields[ field_no ] == null ) {
			throw new ExceptionMessage( "Field " + field_no + " was asked for, but not defined" );
		}
		date_last_modified = new Date();
		fields[ field_no ].setBinary( p_value );
	}

	public void setBinary( String field, byte[] p_value )
	throws ExceptionMessage {
		setBinary(getFieldByName(field), p_value);
	}

	public void read( InputStream input )
	throws ExceptionMessage, IOException {
		date_last_modified = new Date();
		// Read type
		if (message_type != null) {
			message_type.read( input );
		}
		// Read header
		fields[ 0 ].read( input );
		// Read 1-63
		long header = Utils.convertBinToLong( fields[ 0 ].getBytes() );
		long firstfield;
		int fieldnum;
		for ( firstfield = 0x8000000000000000L, fieldnum = 1;firstfield != 0;firstfield >>>= 1 , fieldnum++ ) {
			// Set and read fields
			if ( ( header & firstfield ) == firstfield ) {
				// Do we have a field for this field?
				if ( fields[ fieldnum ] == null ) {
					throw new ExceptionMessage( "Field " + fieldnum + " was found in message, but not expected" );
				}
				//System.out.println("Reading Field:"+fieldnum);
				fields[ fieldnum ].read( input );
			} else {
				// Empty current fieldnum
				if ( fields[ fieldnum ] != null ) {
					fields[ fieldnum ].set((String)null);
				}
			}
		}
		// Was a 2nd bitmap supplied?
		if ( fields[ 1 ].get() != null ) {
			header = Utils.convertBinToLong( fields[ 1 ].getBytes() );
			for ( firstfield = 0x8000000000000000L;firstfield != 0;firstfield >>>= 1 , fieldnum++ ) {
				// Set and read fields
				if ( ( header & firstfield ) == firstfield ) {
					// Do we have a field for this field?
					if ( fields[ fieldnum ] == null ) {
						throw new ExceptionMessage( "Field " + fieldnum + " was found in message, but not expected" );
					}
					//System.out.println("Reading Field:"+fieldnum);
					fields[ fieldnum ].read( input );
				} else {
					// Empty current fieldnum
					if ( fields[ fieldnum ] != null ) {
						fields[ fieldnum ].set((String)null);
					}
				}
			}
		}
	}

	public void write( OutputStream output )
	throws ExceptionMessage, IOException {
		// Set 2nd  field header values
		long header1 = 0;
		long header2 = 0;
		long firstfield;
		int fieldnum;
		for ( firstfield = 0x8000000000000000L, fieldnum = 1;firstfield != 0;firstfield >>>= 1 , fieldnum++ ) {
			if ( fields[ fieldnum + 64 ] != null && !fields[ fieldnum + 64 ].isNull()) header2 |= firstfield;
		}
		if ( fields[ 1 ] == null && header2 != 0 ) {
			throw new ExceptionMessage( "Field definition for field 1 is needed, but not defined" );
		}

		// Write in 2nd headers
		if ( header2 == 0 ) {
			fields[ 1 ].setBinary( null );
		} else {
			fields[ 1 ].setBinary( Utils.convertLongToBin( header2 ) );
		}
		// Get first header
		for ( firstfield = 0x8000000000000000L, fieldnum = 1;firstfield != 0;firstfield >>>= 1 , fieldnum++ ) {
			if (fields[ fieldnum ] != null && !fields[ fieldnum ].isNull()) header1 |= firstfield;
		}
		if ( fields[ 0 ] == null ) {
			throw new ExceptionMessage( "Field definition for field 0 has been tampered with" );
		}

		// Write in headers
		fields[ 0 ].setBinary( Utils.convertLongToBin( header1 ) );

		// Write message type first
		if (message_type != null) {
			//System.out.println("MessageType="+message_type.get());
			message_type.write( output );
		}

		// Write out fields
		for ( int t = 0;t < save_fields;t++ ) {
			if ( fields[t]!=null && !fields[t].isNull()) {
				fields[ t ].write( output );
			}
		}
	}
}

